#include <string.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <unistd.h>
#include <netinet/in.h>
#include <netinet/ip.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include "configure.h"
#include "conf.h"
#include "fstring.h"

#define CONF_PATH "/usr/local/traker/conf.cfg"

void build_cache_list(struct configure *configure)
{
	int i;
	for (i=0; i<configure->nr_cache; i++)
	{
		memset(&configure->cache_list[i], 0, sizeof(struct cache_node));
		configure->cache_list[i].ip = inet_addr(configure->node_str[i]);
	}
}

void pinfo(struct system_info *info)
{
	printf("%d %d\n", info->av1, info->av15);
	printf("%d %d %d %d %d\n", info->us, info->ni, info->sy, info->id, info->wa);
	printf("%d %d %d %d\n", info->mem_tt, info->mem_us, info->mem_fr, info->mem_bu);
	{
		int i;
		for (i=0; i<info->np; i++)
		{
			struct partition *p = &info->p[i];
			printf("%s\n", p->type);
			printf("%s\n", p->mount_point);
			printf("%d %d\n", p->total, p->free);
		}
	}
}
int load_configure(struct configure *configure)
{
	memset(configure, 0, sizeof(struct configure));
	ReadConfig(CONF_PATH, "cache_list", configure->buff_node);
	configure->nr_cache = split(configure->node_str, configure->buff_node, ", ");
	build_cache_list(configure);
	update_cache_state(configure);
	weigth_algorithm(configure);

	return 1;
}


int ask_cache(u_int32_t ip, struct system_info *info)
{
	int sock;
	struct sockaddr_in addr;
	struct timeval tvsnd = {10,0}; 
	struct timeval tvrcv = {5,0};

	memset(&addr, 0, sizeof(addr));
	addr.sin_family = AF_INET;
	addr.sin_port = htons(82);
	addr.sin_addr.s_addr = ip;
	
	if((sock = socket(PF_INET, SOCK_STREAM, 0)) < 0)
		return -1;

	if (setsockopt(sock, SOL_SOCKET,SO_SNDTIMEO, (char *)&tvsnd,sizeof(struct timeval)) < 0)
		perror("setsockopt\r\n");
	if (setsockopt(sock, SOL_SOCKET,SO_RCVTIMEO, (char *)&tvrcv,sizeof(struct timeval)) < 0)
		perror("setsockopt\r\n");

	if(connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0)
		goto err;

	if(recv(sock, info, sizeof(*info), MSG_WAITALL) != sizeof(*info))
		goto err;

	if (memcmp(info->magic, MAGIC, MAGIC_LEN) != 0)
		goto err;

	close(sock);
	return 1;
err:
	close(sock);
	return -1;
}

int av_space(struct system_info *info)
{
	int i, sum = 0;

	for (i=0; i<info->np; i++)
		sum += info->p[i].free;
	return sum;

}

#define should_go_public(cache) (cache->state == ST_PRESENT && cache->total_av > 0)
void smoothing_weight(struct configure *configure)
{
	int i;
	int total_weight = 0;
	int num = 0;
	int avg;
	struct cache_node *cache;

	for (i=0; i<configure->nr_cache; i++)
	{
		cache = &configure->cache_list[i];
		if(!should_go_public(cache))
			continue;
		total_weight += cache->weight;
		num ++;
	}

	if (num == 0)
		return;

	avg = total_weight / num;

	for (i=0; i<configure->nr_cache; i++)
	{
		cache = &configure->cache_list[i];
		if(!should_go_public(cache))
			continue;
		cache->weight = (cache->weight + avg)/2;
	}

}
void weigth_algorithm(struct configure *configure)
{
	int i;
	struct cache_node *cache;

	/* initialize weight */
	for (i=0; i<configure->nr_cache; i++)
	{
		cache = &configure->cache_list[i];
		if(!should_go_public(cache))
			continue;
		cache->weight = cache->total_av;
	}

	/* smoothing weight 2 times, you can smoothing as many times as necessary */
	smoothing_weight(configure);
	smoothing_weight(configure);

	/* sum up weight of all cache */
	configure->total_weight = 0;
	for (i=0; i<configure->nr_cache; i++)
	{
		cache = &configure->cache_list[i];
		if (!should_go_public(cache))
			cache->weight = 0;
		configure->total_weight += cache->weight;
	}

};
int update_cache_state(struct configure *configure)
{
	int i;
	struct cache_node *cache;

	for (i=0; i<configure->nr_cache; i++)
	{
		cache = &configure->cache_list[i];
		if (ask_cache(cache->ip, &cache->sys_info) < 0)
		{
			cache->state = ST_DOWN;
			cache->total_av = 0; 
			continue;
		}
		cache->state = ST_PRESENT;
		cache->total_av = av_space(&cache->sys_info);
	}
	return 1;
}
//int select_master_cache(struct configure *configure)
//{
//	int i, j, k, index = 0;
//	u_int space = 0;
//	u_int rd = rand() % 97;
//	struct cache_node *cache;
//
//	for (i=0; i<configure->nr_cache; i++)
//	{
//		j = (rd +i) % configure->nr_cache;
//		cache = &configure->cache_list[j];
//
//		k = cache->total_av >= 10 && cache->total_av <= 500 ? 11 : cache->total_av;
//
//		/* fix me, temporary used*/
//		if (cache->state == ST_PRESENT && cache->total_av > 0)
//			return j;
//
//		//		if (cache->state == ST_PRESENT && k > space)
//		//		{
//		//			index = j;
//		//			space = k;
//		//		}
//	}
//	return index;
//}

int select_master_cache(struct configure *configure)
{
	int i;
	int total = 0;
	unsigned int rd;
	struct cache_node *cache;

	if (configure->total_weight == 0)
		return 0;

	rd = ((unsigned int)rand()) % configure->total_weight;
	for (i=0; i<configure->nr_cache; i++)
	{
		cache = &configure->cache_list[i];
		total += cache->weight;
		if (rd < total)
			return i; /* will never return a downed cache or 
						 a full cache because its weight is 0 */
	}

	/* we will never be here. create a file /assert so we 
	will known this exactly happen for debug */
	{
		int fd;
		if ((fd = open("/assert", O_WRONLY | O_CREAT)) >= 0 )
			close(fd);
	}
	return 0;
}

int select_master_cache_r(struct configure *configure, pthread_mutex_t *lock)
{
	int ret;
	pthread_mutex_lock(lock);
	ret = select_master_cache(configure);
	pthread_mutex_unlock(lock);
	return ret;

}
int cache_index(struct configure *configure, u_int32_t ip)
{
	int i;
	for (i=0; i<configure->nr_cache; i++)
	{
		if (configure->cache_list[i].ip == ip)
			return i;
	}
	return -1;
}
